package crp.services.api.graphql;

import com.qwlabs.graphql.relay.Connection;
import com.qwlabs.graphql.relay.Relays;
import crp.core.security.Callers;
import crp.core.workflow.app.WorkflowService;
import crp.core.workflow.domain.models.ApprovalOperation;
import crp.core.workflow.domain.models.CreateWorkflowInstanceCommand;
import crp.core.workflow.domain.models.DoneWorkflowTaskCommand;
import crp.core.workflow.domain.models.WorkflowTaskQuery;
import crp.core.workflow.domain.models.WorkflowTaskStatus;
import crp.services.api.graphql.models.CreateWorkflowInstanceInput;
import crp.services.api.graphql.models.WorkflowInstancePayload;
import crp.services.api.graphql.models.WorkflowTaskPayload;
import io.quarkus.panache.common.Range;
import org.eclipse.microprofile.graphql.DefaultValue;
import org.eclipse.microprofile.graphql.GraphQLApi;
import org.eclipse.microprofile.graphql.Id;
import org.eclipse.microprofile.graphql.Mutation;
import org.eclipse.microprofile.graphql.Query;

import javax.annotation.security.PermitAll;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.transaction.Transactional;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.Optional;

@GraphQLApi
@RequestScoped
@Transactional
public class WorkflowGraphQLApi {
    private final WorkflowService workflowService;

    @Inject
    public WorkflowGraphQLApi(WorkflowService workflowService) {
        this.workflowService = workflowService;
    }

    @Query
    @PermitAll
    @NotNull
    public Connection<WorkflowTaskPayload> myTasks(
            Optional<String> keyword,
            String topic,
            WorkflowTaskStatus taskStatus,
            String condition,
            @DefaultValue("0") String after,
            @DefaultValue("10") Integer first) {
        var query = WorkflowTaskQuery.builder()
                .caller(Callers.current())
                .keyword(keyword.orElse(null))
                .condition(condition)
                .taskStatus(taskStatus)
                .workflowTopic(topic)
                .build();
        Range range = Relays.of(after, first);
        return Relays.of(workflowService.engine().by(query, range),
                WorkflowTaskPayload::of);
    }

    @Query
    @PermitAll
    public WorkflowInstancePayload workflowInstance(@NotNull @Valid @Id String id) {
        return workflowService.instanceSearcher().byId(id)
                .map(WorkflowInstancePayload::of)
                .orElse(null);
    }

    @Mutation
    public WorkflowInstancePayload createWorkflowInstance(@NotNull @Valid CreateWorkflowInstanceInput input) {
        CreateWorkflowInstanceCommand command = workflowService.commandCreator()
                .createInstanceCommand(input.getTopic(), input.getExternalId())
                .submittedBy(Callers.userId())
                .build();
        return WorkflowInstancePayload.of(workflowService.engine()
                .create(command));
    }

    @Mutation
    public WorkflowTaskPayload doneWorkflowTask(@NotNull String taskId,
                                                String formAttributes,
                                                @NotNull ApprovalOperation approvalOperation,
                                                @NotNull String comment) {
        DoneWorkflowTaskCommand command = workflowService.commandCreator()
                .createDoneTaskCommand(taskId)
                .comment(comment)
                .operation(approvalOperation)
                .formAttributes(formAttributes).build();
        workflowService.engine().doneTask(command);
        return WorkflowTaskPayload.of(command.getTask());
    }
}
